home *** CD-ROM | disk | FTP | other *** search
Wrap
Text File | 1996-02-19 | 106.7 KB | 3,044 lines | [ TEXT/CWIE]
/* File: AbstractScriptableObject.c Contains: Abstract Scriptable Object support Written by: Greg Anderson Copyright: © 1993-1995 by Apple Computer, Inc, all rights reserved. <31> 9/26/95 pco */ #ifdef MWTRACEBACKTABLES #pragma traceback on #endif #include "AbstractScriptableObject.h" // // Lists of abstract scriptable objects // #include "ScriptableObjectList.h" // // Mark tokens are needed to silently union together multiple // matches in AccessByName // #include "MarkToken.h" // // EveryItemProxy is needed to handle kAEAll in AccessByOrdinal // and pContents in AccessByProperty // #include "EveryItemProxy.h" // // Entire contents is needed to handle pEntireContents in AccessByProperty // #include "EntireContents.h" // // Collectors are needed in AccessBySearchSpec // #include "AbstractCollector.h" // // AbstractSearchSpec is needed to resolve whose descriptors // in ParseWhoseDescriptor, ParseLogicalDescriptor & c. // #include "AbstractSearchSpec.h" // // GenericSearchSpec is needed because we will create a // generic search specification CreateSearchSpecification // to cover cases where the class derived from TAbstractScriptableObject // does not know how to create an optimized specification // #include "GenericSearchSpec.h" // // Comparison operands are needed to make a generic search spec. // #include "ComparisonOperand.h" // // Comparison operands might be object specifiers that we need to // resolve (recursively) // #include "ResolveObjectSpecifier.h" // // We will sometimes be asked to retrieve "any" item, // in which case we will need to generate random numbers. // #include "PseudoRandom.h" // // We include FinderRegistry to get pEntireContents. // This is somewhat undesirable; we should move // pEntireContents to some other .h file. // // #include "FinderRegistry.h" #define pEntireContents 'ects' #define pProperties 'prps' // The "properties" property #include <AERegistry.h> #include <AEObjects.h> // // For memcpy // //#include <string.h> #include "Exceptions.h" #include "Debug.h" //======================================================================================== // CLASS TClassPropertyInfo //======================================================================================== //---------------------------------------------------------------------------------------- // TClassPropertyInfo::PropertyExists //---------------------------------------------------------------------------------------- Boolean TClassPropertyInfo::PropertyExists(unsigned long propertyName) const { long i = 0; if(fArrayOfPropertyIdentifiers != nil) { while(fArrayOfPropertyIdentifiers[i].PropertyIdentifier() != 0) { if(fArrayOfPropertyIdentifiers[i].PropertyIdentifier() == propertyName) return true; ++i; } } return false; } // TClassPropertyInfo::PropertyExists //---------------------------------------------------------------------------------------- // TClassPropertyInfo::DescriptionOfProperty //---------------------------------------------------------------------------------------- const TPropertyDescription* TClassPropertyInfo::DescriptionOfProperty(unsigned long propertyName) const { long i = 0; if(fArrayOfPropertyIdentifiers != nil) { while(fArrayOfPropertyIdentifiers[i].PropertyIdentifier() != 0) { if(fArrayOfPropertyIdentifiers[i].PropertyIdentifier() == propertyName) return &fArrayOfPropertyIdentifiers[i]; ++i; } } return nil; } // TClassPropertyInfo::DescriptionOfProperty //---------------------------------------------------------------------------------------- // TClassPropertyInfo::NumberOfProperties //---------------------------------------------------------------------------------------- long TClassPropertyInfo::NumberOfProperties() const { return fPropertyCount; } // TClassPropertyInfo::NumberOfProperties //---------------------------------------------------------------------------------------- // TClassPropertyInfo::DescriptionOfNthProperty //---------------------------------------------------------------------------------------- const TPropertyDescription* TClassPropertyInfo::DescriptionOfNthProperty(long propertyNumber) const { // // We reverse the order in which we return the properties because the // order of iteration is usually reversed, and we want the first items // in the list to appear first in the results. // if(propertyNumber < fPropertyCount) return &fArrayOfPropertyIdentifiers[(fPropertyCount - propertyNumber) - 1]; else return nil; } //======================================================================================== // CLASS TAbstractScriptableObject //======================================================================================== // // Note: In addition to the properties described below, TAbstractScriptableObject // also defines pContents and pEntireContents. These are not advertised as properties, // though, as they behave more like accessors and would probably interfere with // requests to get every property of an object (you probably wouldn't want to see the // object's contents or entire contents at that time, and you certainly wouldn't want // to see both!) // TPropertyDescription TAbstractScriptableObject::fPropertiesOfClass[] = { { pName, 0, typeChar, typeChar }, { pClass, kDontIncludeInPropertiesProperty, typeType, typeType }, { pDefaultType, kDontIncludeInPropertiesProperty, typeType, typeType }, { pBestType, kDontIncludeInPropertiesProperty, typeType, typeType }, { pID, 0, typeLongInteger, typeLongInteger }, { pIndex, kDontIncludeInPropertiesProperty, typeLongInteger, typeLongInteger } }; #pragma segment ObjectResident ImplementClassData(TAbstractScriptableObject, clScriptableObject); #pragma segment Foundation //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::TAbstractScriptableObject: //---------------------------------------------------------------------------------------- TAbstractScriptableObject::TAbstractScriptableObject() { } // TAbstractScriptableObject::TAbstractScriptableObject //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::~TAbstractScriptableObject: //---------------------------------------------------------------------------------------- TAbstractScriptableObject::~TAbstractScriptableObject() { } // TAbstractScriptableObject::~TAbstractScriptableObject //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::IAbstractScriptableObject: //---------------------------------------------------------------------------------------- void TAbstractScriptableObject::IAbstractScriptableObject() { #ifdef DEBUG // // Look up the class ID of this object as soon as we are constructed // so that we can initialize the debugging information in TObject. // this->ClassID(); #endif } // TAbstractScriptableObject::IAbstractScriptableObject // // Comment out operator equal for now // #ifdef OPEQU //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::operator=: //---------------------------------------------------------------------------------------- TAbstractScriptableObject& TAbstractScriptableObject::operator=(const TAbstractScriptableObject& rhs) { BlockMoveData((Ptr)&rhs, (Ptr)this , sizeof(*this)); // ••• sizeof this sucks return *this; } // TAbstractScriptableObject::operator= #endif //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::IsDesignator: // // This virtual method is overridden by TAbstractDesignator, so that clients dealing // with a token can identify designators. This is necessary because the memory ownership // rules are different for designators than non-designators; see 'CloneDesignator' and // 'DisposeDesignator' for more information. //---------------------------------------------------------------------------------------- Boolean TAbstractScriptableObject::IsDesignator() { return false; } // TAbstractScriptableObject::IsDesignator //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::DisposeDesignator: // // Whenever a method of TAbstractScriptableObject returns a pointer to another // TAbstractScriptableObject (e.g. Parent(), AccessByIndex() and Create()), the // client code must always call DisposeDesignator when it is done using the object. // This is because the TAbstractScriptableObject returned might actually be // a _designator_ to some other TAbstractScriptableObject (e.g. a class that // designates a range of characters within a text object). The designator is not // an object itself--it is only created to specify some subset of another object, // and its lifetime is limited to the local set of code that specifically referenced // the aforementioned subset of an object. // // Properties of objects are designators--they do not represent the entire object // that they are a property of; instead, they designate one aspect of the object // (such as its name, its size, or its best data type). //---------------------------------------------------------------------------------------- void TAbstractScriptableObject::DisposeDesignator() { if(this->IsDesignator()) delete this; } // TAbstractScriptableObject::DisposeDesignator //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::CloneDesignator: // // This is the inverse method of DisposeDesignator; any routine that returns a designator // must return a _copy_ of any existing designator. This routine will do the right thing // and return a reference to non-designators, and a copy of designators. //---------------------------------------------------------------------------------------- TAbstractScriptableObject* TAbstractScriptableObject::CloneDesignator() { if(this->IsDesignator()) return (TAbstractScriptableObject*)this->Clone(); else return this; } // TAbstractScriptableObject::CloneDesignator //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::ObjectClass: // // The class of the object; for example, the Scriptable Finder knows about items of // class cFile, cFolder, cDisk, cTrash, cAlias, cApplicationFile, cSoundFile, and // quite a few others. // // TAbstractScriptableObject::ObjectClass is pure-virtual, and must be overridden // by all derived classes. // // The recorded class is the class that is returned by BuildObjectSpecifier. Although a scriptable // application will usually know about many different classes of things (see the list // of examples given for ObjectClass, above), it should be more general when returning // object specifiers for its objects, just in case some other application that isn't // quite as smart as yours would like to parse them. For example, the Scriptable Finder // only tends to use the classes cFile, cFolder and cDisk when returning specifiers; // there is no need for most applications to have to deal with 'cSoundFile'. //---------------------------------------------------------------------------------------- DescType TAbstractScriptableObject::ObjectClass(const TAETransaction&, Boolean /*recordedClass = false*/) { return typeWildCard; } // TAbstractScriptableObject::ObjectClass //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::DerivedFromOSLClass: //---------------------------------------------------------------------------------------- Boolean TAbstractScriptableObject::DerivedFromOSLClass(const TAETransaction&, DescType objectClass) { return objectClass == typeWildCard; } // TAbstractScriptableObject::DerivedFromOSLClass //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::Exists: // // Most tokens exist if they were created in the first place. Exceptions include the // selection, which claims not to exist if there are no selected objects. //---------------------------------------------------------------------------------------- Boolean TAbstractScriptableObject::Exists(const TAETransaction&) { return true; } // TAbstractScriptableObject::Exists //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::DirectObjectIterator //---------------------------------------------------------------------------------------- TAbstractObjectIterator* TAbstractScriptableObject::DirectObjectIterator(const TAETransaction&) { return new TSingleObjectIterator(this); } // TAbstractScriptableObject::DirectObjectIterator //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::ConvertThisToList // // For a single scriptable object, this method returns a list with one element. // For collection classes, this method returns all of the elements of the collection // in a list. //---------------------------------------------------------------------------------------- AListOf<TAbstractScriptableObject*>* TAbstractScriptableObject::ConvertThisToList(const TAETransaction& t, DescType useRepresentative /* = 0 */) { TAbstractObjectIterator* iter = this->DirectObjectIterator(t); AListOf<TAbstractScriptableObject*>* itemList = new AListOf<TAbstractScriptableObject*>(iter->CountElements(t, typeWildCard)); TAbstractScriptableObject* token = nil; for(iter->Reset(t); iter->More(t); iter->Next(t)) { token = iter->Current(t); if(token->SendCommandToRepresentative(t, useRepresentative)) { TAbstractScriptableObject* ownerOfRepresentative = token; token = ownerOfRepresentative->RepresentativeScriptingObject(t); ownerOfRepresentative->DisposeDesignator(); } itemList->Add(token); } iter->Release(); return itemList; } // TAbstractScriptableObject::ConvertThisToList //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::ObjectRepresentedInToken: // // Returns true if 'objectToTestForMembership' is one of the objects represented by // this token. Trivial for the base case (comaring one TIcon* with another, for // example), but see the implementation in TMarkToken for a better example. One // example of the use of this method is in TSelectionToken, where setting the selection // tests to see if any of the currently selected items are in the list of items // that are about to be selected (usually represented by a TMarkToken). // // This function could be written in a non-virtual manner by calling // NumberOfTokenDirectObjects and GetIndexedTokenDirectObject, but that would // introduce two problems: (1) Mark tokens that contained designators would then // be doing memory allocations and deallocations inside of an O(N^2) loop, and // (2) doing address comparisons on designators isn't particularly meaningful. // Currently, we don't need to do membership tests on designators, though. //---------------------------------------------------------------------------------------- Boolean TAbstractScriptableObject::ObjectRepresentedInToken(const TAETransaction& t, TAbstractScriptableObject* objectToTestForMembership) { TAbstractObjectIterator* iter = this->DirectObjectIterator(t); Boolean isContained = iter->Contains(t, objectToTestForMembership); iter->Release(); return isContained; } // TAbstractScriptableObject::ObjectRepresentedInToken //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::ObjectsAreTheSame: //---------------------------------------------------------------------------------------- Boolean TAbstractScriptableObject::ObjectsAreTheSame(const TAETransaction&, TAbstractScriptableObject* objectToTest) { return (StripAddress((void*)((char*)this - (char*)objectToTest)) == 0); } // TAbstractScriptableObject::ObjectsAreTheSame //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::AdoptToken: //---------------------------------------------------------------------------------------- TAbstractScriptableObject* TAbstractScriptableObject::AdoptToken(TAbstractScriptableObject* token, TypeOfMarkToken markType) { TMarkToken* markToken = new TMarkToken(markType); markToken->IMarkToken(); // // See the comment in AddThisToMarkToken, below, for a description of // how this works. This code is reached in 'case b', below. // 'this' comes from 'markToken', and 'token' came from the contents of // 'this' in AddThisToMarkToken. // markToken->AdoptToken(this, markType); markToken->AdoptToken(token, markType); return markToken; } // TAbstractScriptableObject::AdoptToken //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::AddThisToMarkToken: // // On entry, 'markToken' can be nil, a single token (a TAbstractScriptableObject) // or a Mark token. The desired behavior is: // // a. If markToken is nil on input, it equals 'this' on output // b. If markToken is a single TAbstractScriptable object on input, then // it should be a TMarkToken on output, and its contents should be // 'markToken' (its value on input) and 'this' // c. If markToken is a TMarkToken, then this token should be added to // it (as the name of this method implies). // // This method works in conjunction with TAbstractScriptableObject::AdoptToken. A // double-dispatch is done to avoid doing a downcast to TMarkToken. In 'case b', // above, TAbstractScriptableObject::AdoptToken will be called; it will create a // new TMarkToken and add markToken and this to it. In 'case c', above, // TMarkToken::AdoptToken is called, which simply adds this token to the mark's // list of tokens, and returns the mark token back again. // // IMPORTANT: This routine passes ownership of 'this' to the provided mark token. // This token might be deleted as a result (see TMarkToken::AddThisToMarkToken)! // Therefore, any code that calls AddThisToMarkToken must make sure not to reference // the token passed into the mark token after it is added to the mark list. //---------------------------------------------------------------------------------------- void TAbstractScriptableObject::AddThisToMarkToken(TAbstractScriptableObject*& markToken, TypeOfMarkToken markType) { if(markToken == nil) { // // If the mark type indicates that we should always // make a collection, then adopt a nil token to // create a mark token with one element in it // if(markType == kAlwaysMakeCollection) { markToken = new TMarkToken(markType); ((TMarkToken*)markToken)->IMarkToken(); markToken->AdoptToken(this, markType); } else markToken = this; } else { markToken = markToken->AdoptToken(this, markType); } } // TAbstractScriptableObject::AddThisToMarkToken //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::ReturnReferencedObject: // // This is for the benefit of objects derived from TAbstractReference, which should // override this method to return the actual object that is referenced. Objects that // are not references return themselves (or a copy of themself). //---------------------------------------------------------------------------------------- TAbstractScriptableObject* TAbstractScriptableObject::ReturnReferencedObject() { return this->CloneDesignator(); } // TAbstractScriptableObject::ReturnReferencedObject //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::CreateReference: // // An abstract reference is a lightweight representation of some scriptable object; // References are created when the object itself is large &/or slow to construct, // or to provide a portable (rather than application-specific) description of an object. // // For example, the Finder has HFS references that contain an FSSpec; these references // are used in place of full Finder-HFS objects when searching. // // Question: Does this method really belong here? References will never make // references to themselves, and I don't think that designators will ever have // references either. Still, at this point it is somewhat unclear how specific // this method should be, so a very general base implementation is provided. //---------------------------------------------------------------------------------------- TAbstractReference* TAbstractScriptableObject::CreateReference() { FailErr(errAEEventNotHandled); return nil; } // TAbstractScriptableObject::CreateReference //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::RepresentativeScriptingObject // // A representative scripting object, if provided, will be used to access // properties, elements & c. from a scriptable object if said object cannot // access the property itself. For example, if much of the window scripting // code is contained in a window token class, the actual window object may // wish to create a window token to handle its script requests. // The method SendCommandToRepresentative controls which commands go to // your representative and which are sent to you. (Question: why not just // have TAbstractScriptableObject::Command send the command along if it // is not handled by the primary object?) //---------------------------------------------------------------------------------------- TAbstractScriptableObject* TAbstractScriptableObject::RepresentativeScriptingObject(const TAETransaction&) { return nil; } //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::SendCommandToRepresentative // // Someday, perhaps, this will be virtual, but for now "reveal" is the only command // that we do not want to send to the object's representative. // // n.b. Currently, TProxyToken::AECommandDispatch requires that any given // command either be sent to all of the representative objects or to none of them, // so that routine will need to be rewritten if this method allows each object to // decide if it will invoke its representative or not. //---------------------------------------------------------------------------------------- Boolean TAbstractScriptableObject::SendCommandToRepresentative(const TAETransaction&, long aeCommandID) { return (aeCommandID != kAEMakeObjectsVisible) && (aeCommandID != 0); } // TAbstractScriptableObject::SendCommandToRepresentative //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::ParentObject: // // For now, nil means the null container. In time, we should make a null container // that is based on TAbstractScriptableObject. //---------------------------------------------------------------------------------------- TAbstractScriptableObject* TAbstractScriptableObject::ParentObject(const TAETransaction&) { return nil; } // TAbstractScriptableObject::ParentObject //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::ElementIterator //---------------------------------------------------------------------------------------- TAbstractObjectIterator* TAbstractScriptableObject::ElementIterator(const TAETransaction& t) { TAbstractObjectIterator* iter = nil; TAbstractScriptableObject* representative = this->RepresentativeScriptingObject(t); if(representative != nil) { iter = representative->ElementIterator(t); representative->DisposeDesignator(); } return iter; } // TAbstractScriptableObject::ElementIterator //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::CountElements: //---------------------------------------------------------------------------------------- long TAbstractScriptableObject::CountElements(const TAETransaction& t, DescType classToCount) { long count = 0; TAbstractObjectIterator* iter = this->ElementIterator(t); if(iter != nil) { count = iter->CountElements(t, classToCount); iter->Release(); } return count; } // TAbstractScriptableObject::CountElements //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::AccessByIndex: // // Abstract method //---------------------------------------------------------------------------------------- TAbstractScriptableObject* TAbstractScriptableObject::AccessByIndex(const TAETransaction& t, DescType desiredClass, long index) { TAbstractScriptableObject* result = nil; TAbstractObjectIterator* iter = this->ElementIterator(t); if(iter != nil) { result = iter->GetIndexedElement(t, desiredClass, index); iter->Release(); } return result; } // TAbstractScriptableObject::AccessByIndex //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::AccessByName: // // Abstract method //---------------------------------------------------------------------------------------- TAbstractScriptableObject* TAbstractScriptableObject::AccessByName(const TAETransaction& t, DescType desiredClass, TDescriptor nameDesc) { TAbstractScriptableObject* result = nil; TAbstractObjectIterator* iter = this->ElementIterator(t); if(iter != nil) { result = iter->GetNamedElement(t, desiredClass, nameDesc); iter->Release(); } return result; } //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::AccessByProperty: //---------------------------------------------------------------------------------------- TAbstractScriptableObject* TAbstractScriptableObject::AccessByProperty(const TAETransaction& t, DescType propertyIdentifier) { TAbstractScriptableObject* propertyObject = nil; switch(propertyIdentifier) { // // "contents of..." is the same as "every <<wildcard>> of..." // case pContents: { propertyObject = new TEveryItemProxy; ((TEveryItemProxy*)propertyObject)->IEveryItemProxy(this, typeWildCard); } break; // // "entire contents of..." is like "contents of...", but // it includes items deep inside the child heirarchy of // the specified container, whereas "contents of..." includes // only the immediate children. // case pEntireContents: { propertyObject = new TEntireContents; ((TEntireContents*)propertyObject)->IEntireContents(this); } break; // // "properties of..." returns all of the properties of the // specified object; multiple properties may be set as well. // case pProperties: { // // The default type for the properties property is "typeWildCard," which indicates // that the default type of each property should be returned. Similarly, typeBest // means return the best data type for each property, and typeNull means just return // descriptions of the properties, and skip the actual data. Any other data type // can be requested; in that case, only the properties that can return that data // type are provided. // // Of course, the data type returned for the properties property is _always_ typeAERecord. // propertyObject = new TGenericProperty; ((TGenericProperty*)propertyObject)->IGenericProperty(this, TPropertyDataDescription(pProperties, 0, typeWildCard, typeBest)); } break; } // // If we have not already created a property object, then // look up this property name in our class data and generate // a generic property token for it if it's found. // if(propertyObject == nil) { const TPropertyDescription* propertyDescription = this->DescriptionOfProperty(t, propertyIdentifier); if((propertyDescription != nil) && (NeverCreateGenericProperty(propertyDescription->AdditionalInfo()) == false)) { ASSERT(propertyDescription->PropertyIdentifier() == propertyIdentifier); propertyObject = new TGenericProperty; ((TGenericProperty*)propertyObject)->IGenericProperty(this, *propertyDescription); } else { TAbstractScriptableObject* representative = nil; NOREGISTER(representative); OSErr representativeError = noErr; Try { representative = this->RepresentativeScriptingObject(t); if(representative != nil) propertyObject = representative->AccessByProperty(t, propertyIdentifier); } Catch(representativeError) { } if(representative != nil) representative->DisposeDesignator(); if(propertyObject == nil) FailErr(errAENoSuchObject); } } return propertyObject; } // TAbstractScriptableObject::AccessByProperty //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::Access: // // The access method dispatches to the appropriate accessor (AccessByName, AccessByIndex, // AccessByProperty, etc.). It also knows how to do formRelativePostion, formRange and // formOrdinal, provided that all of the token objects know about the property pContainer, // and the method 'ItemIndex' (AccessByProperty(pIndex)) is also supported. //---------------------------------------------------------------------------------------- TAbstractScriptableObject* TAbstractScriptableObject::Access(const TAETransaction& t, DescType desiredClass, DescType keyForm, TDescriptor keyData ) { TAbstractScriptableObject* resultToken = nil; TDescriptor indexDescriptor; TDescriptor indexDescriptor2; OSErr err = noErr; switch( keyForm ) { case formAbsolutePosition: { // // formAbsolutePosition can contain either: // o A positive integer (offset from beginning) // o A negative integer (offset from end) // o An ordinal (kAEFirst, kAEAny, etc.) // // Most of the ordinals (every one except kAEAll) are // handled by the abstract base class TTokenObject. // Similarly, negative indices are converted to an appropriate // positive value by TTokenObject::AdjustNegativeIndex. // if(keyData.DescriptorType() == typeAbsoluteOrdinal) { DescType ordinal = keyData.GetDescTypeData(typeAbsoluteOrdinal); resultToken = this->AccessByOrdinal(t, desiredClass, ordinal); } else { long index = keyData.GetSInt32Data(); // // AdjustNegativeIndex just subracts the index from // one greater than the number of children in this object // (so an index of -1 specifies the last object, -2 specifies // the second to last object, and so on). // // Note: There never seemed to be a need to override // AdjustNegativeIndex, so I placed its implementation // here and removed the virtual. // if(index < 0) index = this->CountElements(t, desiredClass) + 1 + index; resultToken = this->AccessByIndex(t, desiredClass,index); } } break; case formUniqueID: { resultToken = this->AccessByUniqueID(t, desiredClass, keyData); } break; case formName: { resultToken = this->AccessByName(t, desiredClass,keyData); } break; case formPropertyID: { DescType propertyName = keyData.GetDescTypeData(); ASSERT(desiredClass == cProperty); resultToken = this->AccessByProperty(t, propertyName); } break; case formRelativePosition: { if(keyData.DescriptorType() != typeEnumeration) { FailErr(errAEEventNotHandled); } indexDescriptor = this->GetProperty(t, pIndex, typeLongInteger); long indexNumber = indexDescriptor.GetSInt32Data(); DescType ordinal = keyData.GetDescTypeData(typeEnumeration); TAbstractScriptableObject* parent = nil; short delta; // // We need a reference to our parent in // order to access our siblings // parent = this->ParentObject(t); if(parent == nil) FailErr(errAENoSuchObject); long siblings = parent->CountElements(t); // // The ordinal can be one of: // // o kAENext // o kAEPrevious // if((ordinal == kAENext) || (ordinal == kAEAfter)) { delta = 1; indexNumber += delta; } else if((ordinal == kAEPrevious) || (ordinal == kAEBefore)) { delta = -1; indexNumber += delta; } else if(ordinal == kAEBeginning) { delta = 1; indexNumber = 1; } else if(ordinal == kAEEnd) { delta = -1; indexNumber = siblings; } else { FailErr(errAEBadKeyForm); } //DebugPrintf("delta = %d", delta); // // Advance the index number until we get to a // token of the correct class // while((indexNumber > 0) && (indexNumber <= siblings)) { //DebugPrintf("index now = %d", indexNumber); TAbstractScriptableObject* token = parent->AccessByIndex(t, typeWildCard, indexNumber); //DebugStr("\pGot an object"); // // If the current token is of the correct type, // then add it into the resultToken. // if(token->DerivedFromOSLClass(t, desiredClass)) { //DebugStr("\pIts type was good"); resultToken = token; break; } // // If we don't accumulate the token, then // we must dispose of it // else { //DebugStr("\pWe did not want it"); token->DisposeDesignator(); token = nil; } // // Try the next one // indexNumber += delta; } //DebugStr("\pGetting rid of parent"); parent->DisposeDesignator(); indexDescriptor.Dispose(); } break; // // An interesting thing about formRange: // // The script item 1 through item 2 of document 1 whose name contains "e" // will compile down to items 1 through 2 of document 1 whose name contains "e", // but will use formRange rather than using a whoseRange with the whose // clause. If you actually compile the script items 1 through 2 of document // 1 whose name contains "e", on the other hand, the result LOOKS the same, // but AppleScript will use a whoseRange in conjunction with the whose clause. // case formRange: { TDescriptor clonedKeyData; TDescriptor rangeStop; TDescriptor rangeStart; TDescriptor startContainer; TDescriptor stopContainer; TTokenDescriptor startTokenDesc; TTokenDescriptor stopTokenDesc; TAbstractScriptableObject* startToken = nil; TAbstractScriptableObject* stopToken = nil; DescType tempDesiredClass; DescType tempKeyForm; TDescriptor tempKeyData; Try { // // Get the rangeStart and rangeStop parameters; make sure // they are returned as AERecords. // clonedKeyData = keyData.Coerce(typeAERecord); rangeStart = clonedKeyData.GetDescriptorParameter(keyAERangeStart); rangeStop = clonedKeyData.GetDescriptorParameter(keyAERangeStop); // // At this point, rangeStart is a special object specifier // of the form "item N of «token we previously generated»" // (assuming the original expression was "item N through // item M of document 1 whose name contains "e""); however, // the range stop descriptor will be of the form // "item M of document 1". This is QUITE incorrect, as // item M of document 1 may not have an e in its name, // and it almost certainly is not the Mth item with an // e (unless the first M items all have e's). Therefore, // we may recursively call Resolve to determine the start // token, but we need to do more work to find the stop token. // // Even though we could just call "resolve" on the rangeStart // token, we won't bother to do that just for the sake // of consistancy. // rangeStart.GetObjectSpecifierParameters(tempDesiredClass, tempKeyForm, &tempKeyData); startToken = this->Access(t, tempDesiredClass, tempKeyForm, tempKeyData); tempKeyData.Dispose(); // // Do the same resolution on the range stop (in this // instance, calling resolve is NOT an option) // rangeStop.GetObjectSpecifierParameters(tempDesiredClass, tempKeyForm, &tempKeyData); stopToken = this->Access(t, tempDesiredClass, tempKeyForm, tempKeyData); tempKeyData.Dispose(); // // Note that at this point, "this" is not necessarily // the parent of the start and stop token; it may, for // example, be the collection that resulted from evaluating // a whose clause (as in the above example). Regardless, // the start and stop tokens will always be elements // of "this." // Boolean adding = false; Boolean willStopAdding = false; TAbstractObjectIterator* iter = this->ElementIterator(t); for(iter->Reset(t); iter->More(t); iter->Next(t)) { TAbstractScriptableObject* token = iter->Current(t); Boolean isStart = startToken->ObjectsAreTheSame(t, token); Boolean isStop = stopToken->ObjectsAreTheSame(t, token); if(isStart || isStop) { if((adding == true) || ((isStart) && (isStop))) willStopAdding = true; adding = true; } if((adding) && (token->DerivedFromOSLClass(t, desiredClass))) token->AddThisToMarkToken(resultToken, kSingleItemOrUnion); else token->DisposeDesignator(); if(willStopAdding) adding = false; } } Catch(err) { // // ◊Script: Need to catch and propagate this error // //DebugPrintf("Drat, formRange failed somewhere (err = %d)", Except_Error()); } clonedKeyData.Dispose(); rangeStart.Dispose(); rangeStop.Dispose(); indexDescriptor.Dispose(); indexDescriptor2.Dispose(); startContainer.Dispose(); stopContainer.Dispose(); startTokenDesc.Dispose(); stopTokenDesc.Dispose(); tempKeyData.Dispose(); } break; case formWhose: { TDescriptor scopeOfSearch; TAbstractSearchSpec* searchSpec = this->ParseWhoseDescriptor(keyData, scopeOfSearch); REQUIREVALIDPOINTER(searchSpec); TObjectCollector collector(scopeOfSearch); this->AccessBySearchSpec(t, &collector, desiredClass, searchSpec); // // In some cases, the element iterator returned by the root // object will run asynchronously. If it does, it will // attach a behavior to the collector that will allow us to // wait until the async code completes. // collector.CollectorRequest(t, kWaitForAsyncSearchesToComplete); resultToken = collector.CollectionResult(); scopeOfSearch.Dispose(); } break; default: { FailErr(errAEBadKeyForm); } } return resultToken; } //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::AccessByUniqueID: //---------------------------------------------------------------------------------------- TAbstractScriptableObject* TAbstractScriptableObject::AccessByUniqueID(const TAETransaction& t, DescType desiredClass, TDescriptor uniqueID) { TAbstractScriptableObject* result = nil; TAbstractScriptableObject* representative = nil; NOREGISTER(representative); OSErr representativeError = noErr; Try { representative = this->RepresentativeScriptingObject(t); if(representative != nil) result = representative->AccessByUniqueID(t, desiredClass, uniqueID); } Catch(representativeError) { } if(representative != nil) representative->DisposeDesignator(); FailErr(representativeError); if(result == nil) FailErr(errAEEventNotHandled); return result; } // TAbstractScriptableObject::AccessByUniqueID //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::AccessByOrdinal: // // This method automatically handles kAEFirst, kAELast, kAEMiddle, kAEAny & kAEAll. // All that the derived classes need to do is provide 'CountElements' and 'AccessByIndex' // methods. //---------------------------------------------------------------------------------------- TAbstractScriptableObject* TAbstractScriptableObject::AccessByOrdinal(const TAETransaction& t, DescType desiredClass, DescType ordinal) { TAbstractScriptableObject* resultToken; switch( ordinal ) { case kAEFirst: { resultToken = this->AccessByIndex(t, desiredClass,1); break; } case kAELast: { resultToken = this->AccessByIndex(t, desiredClass,this->CountElements(t, desiredClass)); break; } case kAEMiddle: { resultToken = this->AccessByIndex(t, desiredClass,(this->CountElements(t, desiredClass) + 1) >> 1); break; } case kAEAny: { long totalNumber = this->CountElements(t, desiredClass); resultToken = this->AccessByIndex(t, desiredClass, PseudoRandomFromMToN(1, totalNumber)); break; } case kAEAll: { // // AppleScript sends "properties of ..." as want class cProperty // formAbsoluteOrdinal (kAEAll) from..., so we have special // checking for cProperty here. // if(desiredClass == cProperty) { resultToken = new TGenericProperty; ((TGenericProperty*)resultToken)->IGenericProperty(this, TPropertyDataDescription(pProperties, 0, typeWildCard, typeBest)); } else { TEveryItemProxy* everyItem = new TEveryItemProxy; everyItem->IEveryItemProxy(this, desiredClass); resultToken = everyItem; } } break; default: { ASSERT(false); FailErr(errAEEventNotHandled); break; } } return resultToken; } // TAbstractScriptableObject::AccessByOrdinal //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::AccessBySearchSpec: //---------------------------------------------------------------------------------------- void TAbstractScriptableObject::AccessBySearchSpec(const TAETransaction& t, TAbstractCollector* collector, DescType desiredClass, TAbstractSearchSpec* searchSpec) { // // First, do an entire search, acumulating all results // TAbstractObjectIterator* iter = this->ElementIterator(t); if(iter) { iter->AccessBySearchSpec(t, collector, desiredClass, searchSpec); iter->Release(); } } // TAbstractScriptableObject::AccessBySearchSpec //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::BestType: // // This method returns the best data type for the token (the one that best represents // most of the information stored in the object). The assumption is that the best type // is the default data type of the object, but this will not necessarily be the case // for all token classes. // // RULE: The best type should be the data type that has the highest data fidelity // for your object. For most objects, it should be possible to request the best // type from get data and then coerce the result to any other data type that the // object can return (except typeObjectSpecifier). // // RULE: The best type should be the data type that you want to be used when doing // data comparisons when resolving whose clauses // // RULE: If you expect to EVER be passed an object of typeObjectSpecifier to your // object's set data routine, then your best type MUST be typeObjectSpecifier! If // it isn't, then the scripting code will always attempt to resolve the object // specifier and get your property's best data type from the resulting object. // // RULE: If your object EVER returns an enumeration whose value is the same as // any four-character-code that is defined as a property in ANY terminology // resource (e.g. pName), then your best type MUST be typeEnumeration! // AppleScript will ALWAYS compile your enumeration as an object specifier rather // than an enumeration if it can find a property with the same four-character code // as your enumeration; the scripting code will automatically translate it back // into an enumeration for you before passing it to your set data routine, but // ONLY if your best type is typeEnumeration! Note also that if your enumerations // have the same four-character-code as a property that can return an enumeration // usable by your property (circular dependency), then the scripting code will // not be able to interpret some scripts correctly. //---------------------------------------------------------------------------------------- DescType TAbstractScriptableObject::BestType(const TAETransaction& t, DescType propertyName) { DescType bestType = 0; const TPropertyDescription* propertyData = this->DescriptionOfProperty(t, propertyName); if(propertyData != nil) bestType = propertyData->BestType(); if((bestType == 0) || (BestTypeIsSameAsDefaultType(propertyData->AdditionalInfo()))) bestType = this->DefaultType(t, propertyName); return bestType; } // TAbstractScriptableObject::BestType //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::DefaultType: // // This method returns the data type that is returned by the token if no specific type // is requested. The default default type is typeObjectSpecifier, because all tokens // are expected to be able to build object specifiers for themselves; any token whose // default type is not typeObjectSpecifier should override this method (of course). // // RULE: The default type of your object should always be the most convenient type // for the user (scriptor) to work with when writing simple scripts in the script // editor. //---------------------------------------------------------------------------------------- DescType TAbstractScriptableObject::DefaultType(const TAETransaction& t, DescType propertyName) { DescType defaultType = 0; const TPropertyDescription* propertyData = this->DescriptionOfProperty(t, propertyName); if(propertyData != nil) defaultType = propertyData->DefaultType(); if(defaultType == 0) defaultType = typeObjectSpecifier; return defaultType; } // TAbstractScriptableObject::DefaultType //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::CanReturnDataOfType: // // Every token should override this class and return 'true' if it can return a specified // data type, or call 'inherited' if it does not recognize it. All tokens are expected // to know how to make object specifiers for themselves and handle requests for 'typeBest' // and 'typeWildCard'. A token class does not need to override this method if the only // data types it can return are its default type, its best type and typeObjectSpecifier. //---------------------------------------------------------------------------------------- Boolean TAbstractScriptableObject::CanReturnDataOfType(const TAETransaction& t, DescType propertyName, DescType desiredType) { return ( (desiredType == typeObjectSpecifier) || (desiredType == typeAEList) || (desiredType == this->BestType(t, propertyName)) || (desiredType == this->DefaultType(t, propertyName)) ); } // TAbstractScriptableObject::CanReturnDataOfType //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::DetermineDataTypeToReturn: // // A token object should call this method from its GetData method; if the token object // also overrides 'CanReturnDataOfType', then the return value of this method will be the // data type that the token can generate that the caller most wanted to see. If there is // no match between what the caller wants and what can be provided, then this // method will fail with errAECantSupplyType. // // Also, 'requestedTypes' may be an empty list or a null descriptor; if it is either, // this function will return 'this->DefaultType()' //---------------------------------------------------------------------------------------- DescType TAbstractScriptableObject::DetermineDataTypeToReturn(const TAETransaction& t, DescType propertyName, TDescriptor requestedTypes) { DescType typeToReturn = 0; // // 'CountItems' is smart; it will return '0' for a null descriptor // as well as an empty list. It will return '1' for a descriptor // that is not an AEList. Our descriptor iterator can also iterate over // a single-item TDescriptor. // // The point of this check is: if there was no list of requested types, // or if the list of requested types is empty, then the type to // return is the object's default type // if(requestedTypes.CountItems() == 0) { typeToReturn = this->DefaultType(t, propertyName); } // // If there is a list of requested types, then we must iterate over // them to decide which type to return // else { for(TDescriptorIterator iter(requestedTypes); iter.More(); iter.Next()) { DescType anAcceptableType = iter.GetCurrentDescTypeData(); // // Every object has a best type, so we always // accept 'typeBest' // if(anAcceptableType == typeBest) { typeToReturn = this->BestType(t, propertyName); break; } // // If the requested type is 'any type at all', // then return the default type // if(anAcceptableType == typeWildCard) { typeToReturn = this->DefaultType(t, propertyName); break; } // // If a specific type is requested, return it // if and only if this object can generate // said type. // if(this->CanReturnDataOfType(t, propertyName, anAcceptableType)) { typeToReturn = anAcceptableType; break; } } } // // If we can't return any of the requested types, then spew // if(typeToReturn == 0) { FailErr(errAECantSupplyType); } return typeToReturn; } // TAbstractScriptableObject::DetermineDataTypeToReturn //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::PropertyExists //---------------------------------------------------------------------------------------- Boolean TAbstractScriptableObject::PropertyExists(const TAETransaction& t, DescType propertyName) { const TPropertyDescription* propertyDescription = this->DescriptionOfProperty(t, propertyName); return propertyDescription != nil; } // TAbstractScriptableObject::PropertyExists //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::DescriptionOfProperty //---------------------------------------------------------------------------------------- const TPropertyDescription* TAbstractScriptableObject::DescriptionOfProperty(const TAETransaction& t, DescType propertyName) { TAbstractScriptableObject* representative = this->RepresentativeScriptingObject(t); const TClassData* classData = this->ClassData(); const TPropertyDescription* propertyDescription = nil; while(classData != nil) { const TClassPropertyInfo& propertyInfo = classData->ClassPropertyInfo(); propertyDescription = propertyInfo.DescriptionOfProperty(propertyName); if (propertyDescription != nil) classData = nil; else { if(representative != nil) { classData = representative->ClassData(); representative->DisposeDesignator(); representative = nil; } else classData = classData->SuperClass(); } } return propertyDescription; } // TAbstractScriptableObject::DescriptionOfProperty //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::NumberOfProperties //---------------------------------------------------------------------------------------- long TAbstractScriptableObject::NumberOfProperties(const TAETransaction& t) { TAbstractScriptableObject* representative = this->RepresentativeScriptingObject(t); const TClassData* classData = this->ClassData(); long propertyCount = 0; while(classData != nil) { const TClassPropertyInfo& propertyInfo = classData->ClassPropertyInfo(); propertyCount += propertyInfo.NumberOfProperties(); if(representative != nil) { classData = representative->ClassData(); representative->DisposeDesignator(); representative = nil; } else classData = classData->SuperClass(); } return propertyCount; } // TAbstractScriptableObject::NumberOfProperties //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::DescriptionOfNthProperty // // Normally I would have the range be from 0 to N-1, but to be consistant with the // rest of the scripting code I choose to make the range 1 to N instead. //---------------------------------------------------------------------------------------- const TPropertyDescription* TAbstractScriptableObject::DescriptionOfNthProperty(const TAETransaction& t, long propertyNumber) { TAbstractScriptableObject* representative = this->RepresentativeScriptingObject(t); const TClassData* classData = this->ClassData(); const TPropertyDescription* propertyDescription = nil; // // Adjust the property number so that it will be 0-based // rather than 1-based once we get into the loop. // --propertyNumber; while(classData != nil) { const TClassPropertyInfo& propertyInfo = classData->ClassPropertyInfo(); long numberOfPropertiesForThisClass = propertyInfo.NumberOfProperties(); if(propertyNumber < numberOfPropertiesForThisClass) propertyDescription = propertyInfo.DescriptionOfNthProperty(propertyNumber); else propertyNumber -= numberOfPropertiesForThisClass; if(representative != nil) { classData = representative->ClassData(); representative->DisposeDesignator(); representative = nil; } else classData = classData->SuperClass(); if (propertyDescription != nil) classData = nil; } return propertyDescription; } // TAbstractScriptableObject::DescriptionOfNthProperty //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::GetDataGivenListOfTypes: // // This is the GetData method called by the Get Data event. It determines which // type should actually be returned, and then calls the simpler version of GetData, above. // // New feature: if GetData(DescType requestedType) may now return a descriptor that // is some type other than 'requestedType'; if this happens, then this version of // GetData will attempt to coerce the supplied type into the requested type. // // Formerly, this routine was called 'GetData', but the xLC compiler had trouble // with the polymorphism, so it was renamed. //---------------------------------------------------------------------------------------- TDescriptor TAbstractScriptableObject::GetDataGivenListOfTypes(const TAETransaction& t, DescType propertyName, TDescriptor listOfRequestedTypes, unsigned long additionalInfo /*=kLookupAdditionalInfo*/) { TDescriptor result; // // Find a type that we can return from the list of types that // the client said were acceptable. Remember which type we decided // was best; we'll try to coerce to it if the token returns some // other type. // DescType requestedType = this->DetermineDataTypeToReturn(t, propertyName, listOfRequestedTypes); DescType coerceToType = requestedType; // // If the requested type is 'typeList', then what we really want to // return is a list of the default type of this object. Note that // some objects (mostly proxies, such as the selection) have a default // type of typeList; if this is the case, then we want to return a // list of the best type of the object. // if(requestedType == typeAEList) requestedType = this->DefaultType(t, propertyName); if(requestedType == typeAEList) requestedType = this->BestType(t, propertyName); // // Go ahead and ask for the data now that we've figured out // what sort of type we'd like to see. // result = this->GetDataOfType(t, propertyName, requestedType, additionalInfo); // // We need to try to coerce again, even though GetDataOfType // has already tried. The reason is we may change the requested // type from typeAEList to some other type; if that's // the case, and we don't get a list back, then we need to // coerce the result into a list. // this->CoerceResultToRequestedType(result, coerceToType); return result; } // TAbstractScriptableObject::GetDataGivenListOfTypes //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::GetDataOfType: // // GetDataOfType calls GetData, and if the data type does not match exactly it // does a type coercion, if possible. //---------------------------------------------------------------------------------------- TDescriptor TAbstractScriptableObject::GetDataOfType(const TAETransaction& t, DescType propertyName, DescType requestedType, unsigned long additionalInfo /*=kLookupAdditionalInfo*/) { TDescriptor result; if(additionalInfo == kLookupAdditionalInfo) { const TPropertyDescription* propDescription = this->DescriptionOfProperty(t, propertyName); additionalInfo = propDescription == nil ? 0 : propDescription->AdditionalInfo(); } // // If the requested type is 'typeObjectSpecifier', then build an object // specifier for this object _unless_ the best type of this object is // typeObjectSpecifier. Specifying a best type of typeObjectSpecifier // usually indicates that this object is a proxy for other objects (e.g. // the selection, which returns a list of selected items in response to // a get data event.) // // The reason we bifurcate here rather than allowing TAbstractScriptableObject::GetData // to handle the request for typeObjectSpecifier is that we would like // to allow objects (particularly properties) the option of always returning // their best data type, and allowing this routine to do a coercion if // the requested type was something other than its best type. If the object // had to test for typeObjectSpecifier and call inherited, the benefit of // being able to always return the same data type is largely lost. // if((requestedType == typeObjectSpecifier) && (this->BestType(t, propertyName) != typeObjectSpecifier)) result = this->BuildObjectSpecifierForProperty(t, propertyName); else { result = this->GetProperty(t, propertyName, requestedType, additionalInfo); this->CoerceResultToRequestedType(result, requestedType); } return result; } // TAbstractScriptableObject::GetDataOfType //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::CoerceResultToRequestedType // // This is a method of TAbstractScriptableObject rather than TDescriptor because // I thought perhaps that the best type of the object that this data came from // might sometimes play a part in deciding how to coerce //---------------------------------------------------------------------------------------- void TAbstractScriptableObject::CoerceResultToRequestedType(TDescriptor& result, DescType requestedType) { DescType returnedType = result.DescriptorType(); // // Was the returned type different from the requested type? // if((returnedType != requestedType) && (requestedType != typeWildCard) && (requestedType != typeBest)) { // // Before coercing, test for some situations that are okay // // 1. If the requested type was typeLongInteger, but the result is // typeSInt64, then assume that the data was promoted to an SInt64 // because it would not fit into a long integer // // 2. If the returned type is typeAEList, assume that multiple items // were returned (e.g. get selection), even if some other type // was requested // // 3. Similarly, typeAERecord is okay (pProperties) // // 4. Also, typeNull is okay (can't be coerced--just let it go through). // The caller should have failed if the data weren't available. // // Note the negation in the 'if'; we do the coercion unless one of these // exceptions are true. (I thought it was easier to read this way...) // if( !( ((requestedType == typeLongInteger) && (returnedType == typeSInt64)) || (returnedType == typeAEList) || (returnedType == typeAERecord) || (returnedType == typeNull) ) ) { // // Try to coerce... if coercion fails, then dispose the result // and resignal the failure // OSErr err = result.AttemptCoerceInPlace(requestedType); if(err != noErr) { result.Dispose(); FailErr(err); } } } } // TAbstractScriptableObject::CoerceResultToRequestedType #if 0 //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::GetData: // // GetData treats the requested type as being purely advisory. Call 'GetDataOfType' // to insure that the correct data type is returned if an exact match is required. //---------------------------------------------------------------------------------------- TDescriptor TAbstractScriptableObject::GetData(const TAETransaction& t, DescType requestedType) { return this->GetProperty(t, pContents, desiredType); } // TAbstractScriptableObject::GetData #endif //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::GetDataSizeGivenListOfTypes: //---------------------------------------------------------------------------------------- long TAbstractScriptableObject::GetDataSizeGivenListOfTypes(const TAETransaction& t, DescType propertyName, TDescriptor listOfRequestedTypes) { long theSize = 0; OSErr err = noErr; // // Find a type that we can return from the list of types that // the client said were acceptable // DescType requestedType = this->DetermineDataTypeToReturn(t, propertyName, listOfRequestedTypes); Try { theSize = this->GetDataSize(t, propertyName, requestedType); } Catch(err) { TDescriptor data; data = this->GetDataGivenListOfTypes(t, propertyName, listOfRequestedTypes); if(data.DescriptorType() != typeNull) { theSize = data.DataSize(); } data.Dispose(); } return theSize; } // TAbstractScriptableObject::GetDataSizeGivenListOfTypes //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::GetDataSize: // // Derived classes may override this method to optimize 'GetDataSizeGivenListOfTypes', // above. If 'GetDataSize' is not overridden, then the base class implementation // (this method) will fail, and GetDataSizeGivenListOfTypes will use GetData to determine // how large the data is. //---------------------------------------------------------------------------------------- long TAbstractScriptableObject::GetDataSize(const TAETransaction&, DescType /*propertyName*/, DescType /*requestedType*/) { FailErr(errAEEventNotHandled); return 0; } // TAbstractScriptableObject::GetDataSize //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::SetData: // // Tokens that are not modifiable do not override SetData. //---------------------------------------------------------------------------------------- void TAbstractScriptableObject::SetData(const TAETransaction& t, TDescriptor keyData) { this->SetProperty(t, pContents, keyData); } // TAbstractScriptableObject::SetData //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::GetProperty //---------------------------------------------------------------------------------------- TDescriptor TAbstractScriptableObject::GetProperty(const TAETransaction& t, DescType propertyName, DescType desiredType) { const TPropertyDescription* propDescription = this->DescriptionOfProperty(t, propertyName); return this->GetProperty(t, propertyName, desiredType, propDescription == nil ? 0 : propDescription->AdditionalInfo()); } // TAbstractScriptableObject::GetProperty //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::SetProperty //---------------------------------------------------------------------------------------- void TAbstractScriptableObject::SetProperty(const TAETransaction& t, DescType propertyName, TDescriptor& data) { const TPropertyDescription* propDescription = this->DescriptionOfProperty(t, propertyName); this->SetProperty(t, propertyName, data, propDescription == nil ? 0 : propDescription->AdditionalInfo()); } // TAbstractScriptableObject::SetProperty //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::GetProperty: //---------------------------------------------------------------------------------------- TDescriptor TAbstractScriptableObject::GetProperty(const TAETransaction& t, DescType propertyIdentifer, DescType desiredType, unsigned long additionalInfo) { TDescriptor data; switch(propertyIdentifer) { case pClass: { data.SetDescTypeData(this->ObjectClass(t)); } break; case pBestType: { data.SetDescTypeData(this->BestType(t, pContents)); } break; case pDefaultType: { data.SetDescTypeData(this->DefaultType(t, pContents)); } break; case pIndex: { TAbstractScriptableObject* parent = this->ParentObject(t); long i = 0; if(parent == nil) FailErr(errAENoSuchObject); TAbstractObjectIterator* siblingIterator = parent->ElementIterator(t); for(siblingIterator->Reset(t); siblingIterator->More(t); siblingIterator->Next(t)) { ++i; TAbstractScriptableObject* token = siblingIterator->Current(t); Boolean match = this->ObjectRepresentedInToken(t, token); token->DisposeDesignator(); if(match) { data.SetSInt32Data(i); break; } } if(data.IsNullDescriptor()) FailErr(errAENoSuchObject); } break; case pProperties: { data = this->GetProperties(t, desiredType); } break; case pContents: { if(desiredType == typeObjectSpecifier) data = this->BuildObjectSpecifier(t); else Throw(errAECantSupplyType); } break; default: { TAbstractScriptableObject* propertyToken = nil; TAbstractScriptableObject* representative = nil; OSErr err = noErr; NOREGISTER(propertyToken); NOREGISTER(representative); {Try { // // First, try to get the property from our representative // (if any) // representative = this->RepresentativeScriptingObject(t); if(representative != nil) data = representative->GetProperty(t, propertyIdentifer, desiredType, additionalInfo); else err = errAENoSuchObject; } Catch(err) { }} if(representative != nil) representative->DisposeDesignator(); if(err != noErr) { err = noErr; Try { // // AccessByProperty does not always return a TGenericProperty; // try to generate a property token using AccessByProperty if // an implementation for the specified property does not exist. // This is a little slower, but it's more robust. // // n.b. AccessByProperty will fail with errAENoSuchObject if // the property does not exist. // propertyToken = this->AccessByProperty(t, propertyIdentifer); // // Test for propertyToken being a generic property token. // If it is, then we'll throw ourselves into an infinite loop // when we call GetData! // if(propertyToken->DerivedFrom(clGenericProperty)) { ASSERT(false); Throw(errAENoSuchObject); } data = propertyToken->GetDataOfType(t, pContents, desiredType); } Catch(err) { if(propertyToken != nil) propertyToken->DisposeDesignator(); Throw(err); } } if(propertyToken != nil) propertyToken->DisposeDesignator(); } } return data; } // TAbstractScriptableObject::GetProperty //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::SetProperty: //---------------------------------------------------------------------------------------- void TAbstractScriptableObject::SetProperty(const TAETransaction& t, DescType propertyIdentifer, TDescriptor& data, unsigned long additionalInfo) { switch(propertyIdentifer) { case pClass: case pBestType: case pDefaultType: case pIndex: { Throw(errAENotModifiable); } case pProperties: { this->SetProperties(t, data); } break; default: { TAbstractScriptableObject* propertyToken = nil; TAbstractScriptableObject* representative = nil; OSErr err = noErr; NOREGISTER(propertyToken); NOREGISTER(representative); {Try { // // First, try to get the property from our representative // (if any) // representative = this->RepresentativeScriptingObject(t); if(representative != nil) representative->SetProperty(t, propertyIdentifer, data, additionalInfo); else err = errAENoSuchObject; } Catch(err) { }} if(representative != nil) representative->DisposeDesignator(); if(err != noErr) { err = noErr; Try { // // AccessByProperty does not always return a TGenericProperty; // try to generate a property token using AccessByProperty if // an implementation for the specified property does not exist. // This is a little slower, but it's more robust. // // n.b. AccessByProperty will fail with errAENoSuchObject if // the property does not exist. // propertyToken = this->AccessByProperty(t, propertyIdentifer); // // Test for propertyToken being a generic property token. // If it is, then we'll throw ourselves into an infinite loop // when we call SetData! // if(propertyToken->DerivedFrom(clGenericProperty)) { ASSERT(false); Throw(errAENoSuchObject); } propertyToken->SetData(t, data); } Catch(err) { if(propertyToken != nil) propertyToken->DisposeDesignator(); Throw(err); } } if(propertyToken != nil) propertyToken->DisposeDesignator(); } } } // TAbstractScriptableObject::SetProperty //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::SetProperties: // // Set a whole bunch of properties all with one method call. Currently, this method is // only used by the "Create Element" event. It might be a good idea to have a // "Set Properties" event, though, as there are likely a number of instances where an // application would like to set a large number of properties in the same object or set // of objects without having to pay the IPC & Resolve overhead for each and every // property being set. //---------------------------------------------------------------------------------------- void TAbstractScriptableObject::SetProperties(const TAETransaction& t, TDescriptor& properties) { Boolean atLeastOneWorked = false; OSErr err = noErr; for(TDescriptorIterator iter(properties); iter.More(); iter.Next()) { // DebugNumber("\pBegin loop for property @", keyword); // // ••• What should we do about errors here? If one // of the specified properties does not exist, // cannot be modified or does not accept the // data type provided, we'll get an exception. // However, we would also like to try to set all // of the other properties in the record. Right // now we just swallow errors. If we reported // them, we would have the problem that we might // have more than one error number to return. We // could just return the last error, or perhaps // we could return the error code if only one // property signaled an exception, and invent a // new error code that means "I saw more than one // error go by." // // If we ever did care to report errors even if there // was at least one object that worked correctly, we // might want to consider ignoring 'not modifiable' // errors. // Try { this->SetProperty(t,iter.CurrentKeyword(),iter.Current()); atLeastOneWorked = true; } Catch(err) { } } if(atLeastOneWorked == false) FailErr(err); } // TAbstractScriptableObject::SetProperties //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::GetProperties: // // Get a whole bunch of properties all with one method call. Currently, this method // always returns _all_ published properties for the specified object. In the future // it might be nice if we could include an input parameter that listed the properties // that we were interested in; if unspecified, then all would be returned. // // The desired type is passed on to all of the properties of this class; the // "properties" property _always_ returns an AERecord. //---------------------------------------------------------------------------------------- TDescriptor TAbstractScriptableObject::GetProperties(const TAETransaction& t, DescType desiredType) { TDescriptor properties; TDescriptor data; Boolean atLeastOneWorked = false; OSErr err = noErr; properties.MakeAERecord(); // // Loop from the number of properties down to 1 // so that pName and the other properties of // TAbstractScriptable object will always be // listed first. // long numberOfProperties = this->NumberOfProperties(t); for(long i=numberOfProperties;i>0;--i) { const TPropertyDescription* propertyDescription = this->DescriptionOfNthProperty(t, i); // // Skip any property in the property table that is marked for exclusion // if(DontIncludeInPropertiesProperty(propertyDescription->AdditionalInfo()) == false) { Try { // // Look up the data type to get; usually it will be the default type // or the best type for an individual property, but sometimes another // data type might be specified; in that case, only properties that // can return the specified type will be included in the result. // DescType typeToGet = desiredType; if(desiredType == typeWildCard) typeToGet = propertyDescription->DefaultType(); if(desiredType == typeBest) typeToGet = propertyDescription->BestType(); // // If the type to get is 'typeNull', then a description of the // property without any of its data is returned. // // ◊Script: We should try to coerce the results of 'GetProperty' // to 'typeToGet' after it returns, just like GetDataOfType // does. // if(typeToGet != typeNull) { data = this->GetProperty(t, propertyDescription->PropertyIdentifier(),typeToGet, propertyDescription->AdditionalInfo()); this->CoerceResultToRequestedType(data, typeToGet); } properties.PutDescriptorParameter(propertyDescription->PropertyIdentifier(), data); atLeastOneWorked = true; } Catch(err) { } data.Dispose(); } } if(atLeastOneWorked == false) { properties.Dispose(); FailErr(err); } return properties; } // TAbstractScriptableObject::GetProperties //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::ResolveSpecifierAndGetData // // Given an object specifier, resolve it and call Get Data. //---------------------------------------------------------------------------------------- TDescriptor TAbstractScriptableObject::ResolveSpecifierAndGetData(const TAETransaction& t, TDescriptor objectSpecifier) { TTokenDescriptor sourceToken; TDescriptor resolvedData; OSErr err = noErr; Try { sourceToken = objectSpecifier.Resolve(t); DescType bestTypeForDestination = this->BestType(t, pContents); TAbstractScriptableObject* token = sourceToken.TokenObject(); // // We would like to get data from the resolved token using // the best type of this object; if that is not possible, though, // then we get the best data type of the resolved token, and hope // that it can be coerced into the best type for this object. // DescType dataType = (token->CanReturnDataOfType(t, pContents, bestTypeForDestination) ? bestTypeForDestination : token->BestType(t, pContents) ); resolvedData = token->GetDataOfType(t, pContents, dataType); sourceToken.DisposeToken(); } Catch(err) { sourceToken.DisposeToken(); resolvedData.Dispose(); } return resolvedData; } // TAbstractScriptableObject::ResolveSpecifierAndGetData //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::ResolveKeyData: // // The purpose of this routine is to convert object specifiers passed in as key // data in a 'Set Data' event into data in the best type supported by the token. Other // conversions may be done by other tokens. // // This method should either return a new descriptor (which will be disposed of by // the SetData handler) or a descriptor of typeNull. It should _never_ return // the keyData descriptor itself (although it could return a copy of the keydata, // this is no different than returning typeNull). // // DANGER: The code here is very simple, but the logic is quite complex. Fortunately, // you should never need to override or call ResolveKeyData. //---------------------------------------------------------------------------------------- TDescriptor TAbstractScriptableObject::ResolveKeyData(const TAETransaction& t, TDescriptor keyData) { TDescriptor resolvedData; OSErr err = noErr; Try { // // If the key data is an object specifier, and the best type // of this token (the _destination_ token--the object that // will be receiving the results of the set data event) is not // typeObjectSpecifier, then try to extract data from the source // token (resolved from the keyData object specifier) in the best // data type of the destination token if possible, or in the best // type of the source token if the source cannot provide the // destination's best data type. // // Why check for BestType == typeObjectSpecifier? // // If doing a set data on pSelection, the key data will always // be type object specifier. pSelection expects an object // specifier, though, so it doesn't make sense to try to // resolve the key data here. // if((keyData.DescriptorType() == typeObjectSpecifier) && (this->BestType(t, pContents) != typeObjectSpecifier)) { // // ••• Strange exception: // // Some properties (e.g. the view of a window) accept an // object specifier of a property in place of an enumeration. // For example, "set view of window 1 to name" results in // keyData that is an object specifier whose key data is // pName, and whose parent container is the null container. // This object specifier is treated the same as an enumeration // whose value is pName. // // Any object specifier in the key data whose key form is // keyPropertyID may be converted into an enumeration (GetEnumeration // will do this translation). Note there is an ambiguity // here: is "set x to name" supposed to pass the enumeration // pName to object x, or is it supposed to pass (get data // pName of null container) to object x (the later is the // normal case)? // // We pass the enumeration if and only if the best type // for object x is typeEnumeration. Note, however, that // it is also possible to say "set view of window 1 to // view of window 2"; in this case, ResolveKeyData will // do the wrong thing and pass 'pView' to the set data // method of the "view of window 1" property, which will // fail. The set data handler is smart enough to detect // this situation and correct for it, though. // // Hardest cases: // // tell window 1 of application "Finder" // set view to view of window 2 // end tell // // (This is the case corrected for in the set data handler; // first, view of window 1 is set to 'pView'; this fails, // so set data resolves view of window 2 and tries again.) // // tell window 1 of application "Finder" // set view to name // end tell // // (AppleScript sends an object specifier for "name of // window 1". We assume that "pName" was intended, and // do the right thing.) // // tell application "Finder" // set view of window 1 to name of window 1 // end tell // // (This should be an error, but we confuse it with the // above case and change the view of window 1 to name.) // Boolean shouldDoResolution = true; OSErr unusedErr = noErr; Try { // // If the best type of the object is typeEnumeration, and // we can convert the object specifier in keyData into an // enumeration, then we DO NOT want to resolve the keyData. // if(this->BestType(t, pContents) == typeEnumeration) { DescType resolvedEnumeration = keyData.GetDescTypeData(typeEnumeration); resolvedData.PutDescTypeParameter(resolvedEnumeration, typeEnumeration); shouldDoResolution = false; } } Catch(unusedErr) { } // // In all other cases, resolve the object specifier, call Get Data // on the resulting token, and pass the result on to the object // that SetData is being called on. // if(shouldDoResolution == true) resolvedData = ResolveSpecifierAndGetData(t, keyData); } } Catch(err) { resolvedData.Dispose(); Throw(err); } return resolvedData; } // TAbstractScriptableObject::ResolveKeyData //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::CompareDescriptor: // // This method compares this token with the descriptor provided in 'descOrObject.' // 'descOrObject' can contain either literal data (e.g. a string or an integer), or // it could contain an object specifier to some other object to compare against. // If an object specifier is passed in, then we resolve it and try to get the best // data type for _this_ token; if the comparison token cannot provide that data type, // then we try to get its best data type and do a coercion. //---------------------------------------------------------------------------------------- Boolean TAbstractScriptableObject::CompareDescriptor(const TAETransaction& t, DescType comparisonOperator, TDescriptor& descOrObject) { Boolean comparisonResult = true; TDescriptor ourData; TDescriptor compareWith; OSErr err = noErr; Try { // // Compare with 'descOrObject,' unless we need to coerce it, // in which case we will compare with the coerced descriptor 'compareWith' // TDescriptor* compareWithRef = &descOrObject; // // First, we try to coerce the comparison object to the // best data type for this object, if possible, to make // it more likely that we can obtain the same type of // data from this object. // DescType ourBestType = this->BestType(t, pContents); if((compareWith.DescriptorType() != ourBestType) && (ourBestType != typeWildCard)) { OSErr coerceErr = noErr; compareWith = descOrObject.AttemptToCoerce(this->BestType(t, pContents), coerceErr); // // If we cannot coerce to the best type, try to coerce // to some standard data type so that we will be more // likely to be able to get a compatable data type. // if(coerceErr != noErr) compareWith = descOrObject.CoerceToStandardType(); // // If we were able to coerce to our best type or // to some standard type, then use the coerced data // to do the comparison. // if(compareWith.IsNullDescriptor() == false) compareWithRef = &compareWith; } // // If we can get data from this token in the same data type // as the comparison object, then do so; otherwise, get the // best data type for this object and try to coerce it to // the same type as the data that was passed to us // if(this->CanReturnDataOfType(t, pContents, compareWithRef->DescriptorType())) { ourData = this->GetDataOfType(t, pContents, compareWithRef->DescriptorType()); } else { ourData = this->GetDataOfType(t, pContents, this->BestType(t, pContents)); ourData.CoerceInPlace(compareWithRef->DescriptorType()); } // // Once we have two peices of data in the same type, // doing the comparison is relatively straightforward // // Note that TDescriptor::Compare assumes that the // two objects are of the same type, and fails if // they are not. // comparisonResult = ourData.Compare(comparisonOperator,*compareWithRef); ourData.Dispose(); compareWith.Dispose(); } Catch(err) { ourData.Dispose(); compareWith.Dispose(); Throw(err); } return comparisonResult; } // TAbstractScriptableObject::CompareDescriptor //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::Compare: // // This is one of two 'Compare' methods. This compare method compares this token // with every token in the 'compareWith' parameter. //---------------------------------------------------------------------------------------- Boolean TAbstractScriptableObject::Compare(const TAETransaction& t, DescType comparisonOperator, TAbstractScriptableObject* compareWith) { Boolean comparisonResult = false; TDescriptor compareData; DescType dataTypeToCompare; // // If the token we are comparing with can return our best type, then // ask it for our best type. // if(compareWith->CanReturnDataOfType(t, pContents, this->BestType(t, pContents))) dataTypeToCompare = this->BestType(t, pContents); else dataTypeToCompare = compareWith->BestType(t, pContents); // // GetDataOfType will always return the requested type, or fail. // compareData = compareWith->GetDataOfType(t, pContents, dataTypeToCompare); // // Once we have solid data to compare (not a specifier), // we can call CompareDescriptor // // ◊Script: It would be bad if we got an object specifier back // here, as our technique for resolving it would normally // be to make a recursive call to this "Compare" routine; // however, doing that would throw us into an infinite // loop, because the next iteration would also return an // object specifier. // // The case we are probably concerned with is when we // compare the "selection" to something. Doing this // seems to work without involving our Compare callback, // though. Therefore, we just return 'false' if we // get an object specifier. Perhaps we should fail // with some form of 'not handled' error, though. // if(compareData.DescriptorType() != typeObjectSpecifier) comparisonResult = this->CompareDescriptor(t, comparisonOperator,compareData); compareData.Dispose(); return comparisonResult; } // TAbstractScriptableObject::Compare //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::Compare: // // This method compares this token with the descriptor provided in 'descOrObject.' // 'descOrObject' can contain either literal data (e.g. a string or an integer), or // it could contain an object specifier to some other object to compare against. // If an object specifier is passed in, then we resolve it and try to get the best // data type for _this_ token; if the comparison token cannot provide that data type, // then we try to get its best data type and do a coercion. //---------------------------------------------------------------------------------------- Boolean TAbstractScriptableObject::Compare(const TAETransaction& t, DescType comparisonOperator, TDescriptor& descOrObject) { Boolean comparisonResult = true; Boolean couldGetOSpec = false; OSErr err = noErr; if(descOrObject.DescriptorType() == typeObjectSpecifier) { TTokenDescriptor compareTokenDesc; // // First try: try to resolve the object specifier and compare // this token with the best data from the resolved specifier // Try { compareTokenDesc = descOrObject.Resolve(t); couldGetOSpec = true; } Catch(err) { } // // If we resolved, try to do a get data // if(couldGetOSpec) { Try { comparisonResult = this->Compare(t, comparisonOperator,compareTokenDesc.TokenObject()); } Catch(err) { compareTokenDesc.DisposeToken(); Throw(err); } } compareTokenDesc.DisposeToken(); } // // If we couldn't resolve the specifier (or if it wasn't an object specifier to begin with), // then call the simple compare routine // if(couldGetOSpec == false) { comparisonResult = this->CompareDescriptor(t, comparisonOperator, descOrObject); } return comparisonResult; } // TAbstractScriptableObject::Compare //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::CompareProperty: // // To avoid extra memory allocations (both for 'testDesc', and // possibly another in 'GetProperty', if AccessByProperty/GetData // is called), then this routine may be overridden and a direct // compare may be substituted. Doing this is just a performance // optimization, however; the generic code below is sufficient // to handle any property compare that is needed. //---------------------------------------------------------------------------------------- Boolean TAbstractScriptableObject::CompareProperty(const TAETransaction& t, DescType propertyIdentifier, DescType comparisonOperator, TDescriptor compareWith) { Boolean result = false; Boolean tested = false; // // Some properties are handled in a special way // switch(propertyIdentifier) { case pClass: { // // "class contains cXXX" is an alternate way to // represent "this object is derived from class // cXXX" (we need to support this in case someone // wishes to encode class membership as a term // to test in the search spec rather than pass it // in as a separate parameter. // if(comparisonOperator == kAEContains) { result = this->DerivedFromOSLClass(t, compareWith.GetDescTypeData()); tested = true; } } break; } // // By default, // if(tested == false) { TDescriptor testDesc = this->GetProperty(t, propertyIdentifier, typeWildCard); result = testDesc.Compare(comparisonOperator, compareWith); testDesc.Dispose(); } return result; } // TAbstractScriptableObject::CompareProperty //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::ParseComparisonOperator: //---------------------------------------------------------------------------------------- TComparisonOperand* TAbstractScriptableObject::ParseComparisonOperand(TDescriptor& operand) { TComparisonOperand* result = nil; if(operand.DescriptorType() == typeObjectSpecifier) { // // First, parse the object specifier and determine if it // is relative to the object being examined or not. // TAbstractObjectSpecifier* parsedSpecifier = ParseObjectSpecifier(operand); Boolean isRelativeToObjectBeingExamined = parsedSpecifier->RootContainerIsObjectBeingExamined(); // // Assume that we are going to be comparing against the // 'pContents' property of the specified object. If the // first portion of the object specifier is a property, // however, then we can extract the property from the // specifier and use some of the optimized comparison // code available to us. // DescType propertyToCompare = pContents; if(parsedSpecifier->KeyForm() == formPropertyID) { DescType desiredClass; DescType keyForm; TDescriptor keyData; TAbstractObjectSpecifier* formerSpecifier = parsedSpecifier; parsedSpecifier = formerSpecifier->ExtractParentSpecifier(desiredClass, keyForm, keyData); propertyToCompare = keyData.GetDescTypeData(); // // As a further optimization, we don't need the parsed specifier // if it is just a TObjectBeingExamined; nil is just as good // (better, even, since it doesn't require is to clone the object // being examined, like the TObjectBeingExamined would). // if(parsedSpecifier->KeyForm() == typeObjectBeingExamined) { delete parsedSpecifier; parsedSpecifier = nil; } // // Delete the former specifier now that we've extracted // the property specifier from it. n.b. this also deletes // the descriptor 'keyData'. // delete formerSpecifier; keyData.ClearDescriptor(); } // // Now build an object that knows how to compare a property // of some object // result = new TPropertyComparisonOperand(propertyToCompare, parsedSpecifier, isRelativeToObjectBeingExamined); } else { result = new TConstantComparisonOperand(operand); operand.ClearDescriptor(); } return result; } //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::ParseComparisonOperator: //---------------------------------------------------------------------------------------- TAbstractSearchSpec* TAbstractScriptableObject::ParseComparisonOperator(DescType comparisonOperator, TDescriptor& object1, TDescriptor& object2) { TAbstractSearchSpec* searchSpec = nil; // DescType desiredClass; // DescType keyForm; TDescriptor keyData; TDescriptor containerDesc; OSErr err = noErr; Try { // // We assume that object 1 is an object specifier, and // that object 2 is literal data. This is almost always // going to be the case; however, we do check to see if // object 1 and object 2 need to be reversed, should // object 2 be the object specifier and object 1 be the // literal data. Note that this is not strictly necessary, // but we do it to allow for performance optimizations // involving comparing a property of some object with // literal data, since there are no optimizations when // comparing literal data with a property of some object. // if((object1.DescriptorType() != typeObjectSpecifier) || (object2.DescriptorType() == typeObjectSpecifier)) { TDescriptor tmp; comparisonOperator = ReverseComparisonOperator(comparisonOperator); tmp.AdoptDescriptor(object2); object2.AdoptDescriptor(object1); object1.AdoptDescriptor(tmp); } // // Create comparison operands and a search specification for this // comparison operation. // TComparisonOperand* operand1 = this->ParseComparisonOperand(object1); TComparisonOperand* operand2 = this->ParseComparisonOperand(object2); searchSpec = new TGenericSearchSpec(comparisonOperator, operand1, operand2); } Catch(err) { containerDesc.Dispose(); keyData.Dispose(); Throw(err); } containerDesc.Dispose(); keyData.Dispose(); return searchSpec; } // TAbstractScriptableObject::ParseComparisonOperator //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::ParseLogicalDescriptor: //---------------------------------------------------------------------------------------- TAbstractSearchSpec* TAbstractScriptableObject::ParseLogicalDescriptor(DescType logicalOperator, TDescriptor logicalTerms) { TAbstractSearchSpec* searchSpec = nil; switch(logicalOperator) { case kAENOT: case kAEOR: case kAEAND: { TAbstractSearchSpec* oneSpecification = nil; AListOf<TAbstractSearchSpec*>* specificationList = nil; // // Iterate over all of the terms in the logical descriptor list // for(TDescriptorIterator iter(logicalTerms); iter.More(); iter.Next()) { // // Resolve each term independantly // oneSpecification = this->ParseWhoseTest(iter.Current()); // // Do we have a list of specifications yet? // if(specificationList == nil) { // // I. FIRST TIME THROUGH THE LOOP: // // If this is the first term, just remember it for later // UNLESS the logical operator is NOT, in which case // we always want to make a list of terms // if((searchSpec == nil) && (logicalOperator != kAENOT)) searchSpec = oneSpecification; // // If we already have a term (or two), then we must try // to union the new term with the old term (or one of the // old terms) // else { // // II. SECOND TIME THROUGH THE LOOP // // Make a list and add the two terms to it // specificationList = new AListOf<TAbstractSearchSpec*>; if(searchSpec) specificationList->Add(searchSpec); specificationList->Add(oneSpecification); searchSpec = nil; } } else { // // III. EVERY OTHER TIME THROUGH THE LOOP // // Add the new term to the list // if(oneSpecification != nil) specificationList->Add(oneSpecification); } } // // If we have a specification list, then we need to create // a logical term to hold it // if(specificationList != nil) { searchSpec = new TLogicalSearchSpec(logicalOperator, specificationList); } // // At this point, we'd better have a specifier // if(searchSpec == nil) FailErr(errAEEventNotHandled); } break; default: { FailErr(errAEEventNotHandled); } break; } return searchSpec; } // TAbstractScriptableObject::ParseLogicalDescriptor //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::ParseWhoseTest: //---------------------------------------------------------------------------------------- TAbstractSearchSpec* TAbstractScriptableObject::ParseWhoseTest(TDescriptor& whoseDescriptor) { TAbstractSearchSpec* searchSpec = nil; OSErr err = noErr; switch(whoseDescriptor.DescriptorType()) { case typeLogicalDescriptor: { DescType logicalOperator; TDescriptor logicalTerms; Try { whoseDescriptor.CoerceInPlace(typeAERecord); logicalOperator = whoseDescriptor.GetDescTypeParameter(keyAELogicalOperator, typeEnumeration); logicalTerms = whoseDescriptor.GetDescriptorParameter(keyAELogicalTerms); searchSpec = this->ParseLogicalDescriptor(logicalOperator, logicalTerms); } Catch(err) { logicalTerms.Dispose(); Throw(err); } logicalTerms.Dispose(); } break; case typeCompDescriptor: { DescType compOperator; TDescriptor object1; TDescriptor object2; Try { whoseDescriptor.CoerceInPlace(typeAERecord); compOperator = whoseDescriptor.GetDescTypeParameter(keyAECompOperator, typeEnumeration); object1 = whoseDescriptor.GetDescriptorParameter(keyAEObject1); object2 = whoseDescriptor.GetDescriptorParameter(keyAEObject2); searchSpec = this->ParseComparisonOperator(compOperator, object1, object2); } Catch(err) { //object1.Dispose(); //object2.Dispose(); Throw(err); } //object1.Dispose(); // // ◊Script: at the moment, we adopt 'object2' in GenericSearchSpec // without cloning it. Eventually we'll have to make the appropriate // parameters TDescriptor&, so we can null-out object2 if it is adopted // //object2.Dispose(); } break; default: { FailErr(errAEEventNotHandled); } break; } return searchSpec; } // TAbstractScriptableObject::ParseWhoseTest //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::ParseWhoseDescriptor: //---------------------------------------------------------------------------------------- TAbstractSearchSpec* TAbstractScriptableObject::ParseWhoseDescriptor(TDescriptor whoseDescriptor, TDescriptor& scopeOfSearch) { TAbstractSearchSpec* searchSpec = nil; TDescriptor testDescriptor; TDescriptor whoseRecord; OSErr err = noErr; scopeOfSearch.ClearDescriptor(); // // We assume that the descriptor type of the whose descriptor // will always be typeWhoseDescriptor. // if(whoseDescriptor.DescriptorType() != typeWhoseDescriptor) FailErr(errAEEventNotHandled); Try { whoseRecord = whoseDescriptor.Coerce(typeAERecord); scopeOfSearch = whoseRecord.GetDescriptorParameter(keyAEIndex); testDescriptor = whoseRecord.GetDescriptorParameter(keyAETest); searchSpec = this->ParseWhoseTest(testDescriptor); } Catch(err) { scopeOfSearch.Dispose(); testDescriptor.Dispose(); whoseRecord.Dispose(); Throw(err); } testDescriptor.Dispose(); whoseRecord.Dispose(); return searchSpec; } // TAbstractScriptableObject::ParseWhoseDescriptor //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::BuildObjectSpecifierForProperty //---------------------------------------------------------------------------------------- TDescriptor TAbstractScriptableObject::BuildObjectSpecifierForProperty(const TAETransaction& t, DescType propertyName) { TDescriptor result; TDescriptor specifier = this->BuildObjectSpecifier(t); if(propertyName == pContents) result.AdoptDescriptor(specifier); else { TDescriptor keyData; keyData.SetDescTypeData(propertyName); result.MakeObjectSpecifier(cProperty, specifier, formPropertyID, keyData, true); } return result; } //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::BuildObjectSpecifier: //---------------------------------------------------------------------------------------- TDescriptor TAbstractScriptableObject::BuildObjectSpecifier(const TAETransaction& t) { TDescriptor result; TDescriptor container; DescType desiredClass; DescType keyForm; TDescriptor keyData; // // Build an object specifier for the object that we are contained in. // container = this->BuildSpecifierForParent(t); // // Next, gather together the information about this specific object // that will allow the object accessors to return it should it be // asked for again. // desiredClass = this->ObjectClass(t, kObjectRecordedClass); this->MakeKeyDataForSelf(t, keyForm, keyData); // // Note: if the key form is formPropertyID, then the // desired class must always be cProperty. ObjectClass(kObjectRecordedClass) // always returns cProperty in the case of properties // if((keyForm == formPropertyID) && (desiredClass != cProperty)) { desiredClass = cProperty; } // // Once we have the individual peices of the object specifier, // ask the AEPackObject library to put it all together for us. // result.MakeObjectSpecifier(desiredClass, container, keyForm, keyData, true); return result; } // TAbstractScriptableObject::BuildObjectSpecifier //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::MakeKeyDataForSelf: // // By default, objects are accessed by name. If a given object doesn't have a name, // or if it should be accessed by some other form, then override this method. //---------------------------------------------------------------------------------------- void TAbstractScriptableObject::MakeKeyDataForSelf(const TAETransaction& t, DescType& keyForm, TDescriptor& keyData) { keyForm = formName; keyData = this->GetProperty(t, pName, typeChar); } // TAbstractScriptableObject::MakeKeyDataForSelf //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::BuildSpecifierForParent: // // This method doesn't actually need to be virtual, since it will be very rare for // anyone to ever need to override it, and those who do may override BuildObjectSpecifier // instead. //---------------------------------------------------------------------------------------- TDescriptor TAbstractScriptableObject::BuildSpecifierForParent(const TAETransaction& t) { TAbstractScriptableObject* parent; TDescriptor result; // // If we have a parent, then ask it to build an object // specifier. If we have no parent, then return a null // descriptor to indicate that this object may be accessed // via the null container. // parent = this->ParentObject(t); if(parent != nil) { result = parent->BuildObjectSpecifier(t); parent->DisposeDesignator(); } return result; } // TAbstractScriptableObject::BuildSpecifierForParent //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::BuildSimpleSpecifier //---------------------------------------------------------------------------------------- TDescriptor TAbstractScriptableObject::BuildSimpleSpecifier(const TAETransaction& t) { return this->BuildObjectSpecifier(t); } //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::GetAECommandDispatcher: // // This method allows any object to specify some other object to send specific commands // to; for example, the Finder sends delete commands to the trash object, since // deleting a file with a script moves it to the trash. // // I was somewhat tempted to allow GetAECommandDispatcher to also change the // command ID of the command being redirected, but I decided to keep it simple. //---------------------------------------------------------------------------------------- TAbstractScriptableObject* TAbstractScriptableObject::GetAECommandDispatcher(const TAETransaction&, long /*commandID*/, long /*auxInfo*/, long& oneDispatchSelector) { oneDispatchSelector = 0; return nil; } // TAbstractScriptableObject::GetAECommandDispatcher //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::AECommandDispatch: //---------------------------------------------------------------------------------------- TDescriptor TAbstractScriptableObject::AECommandDispatch(const TAETransaction& t, long aeCommandID, long auxInfo /* = 0 */) { TDescriptor result; long dispatchSelector = 0; // // First, decide if the command will operate on this object // or its representative // TAbstractScriptableObject* representative = nil; if(this->SendCommandToRepresentative(t, aeCommandID) == true) representative = this->RepresentativeScriptingObject(t); TAbstractScriptableObject* noun = representative ? representative : this; // // Next, decide if the command will be dispatched to the // noun, or to a different dispatch object. // TAbstractScriptableObject* dispatcher = noun->GetAECommandDispatcher(t, aeCommandID, auxInfo, dispatchSelector); if(dispatcher == nil) result = noun->AECommand(t, aeCommandID, nil, auxInfo); else { result = dispatcher->AECommand(t, aeCommandID, noun, auxInfo); dispatcher->DisposeDesignator(); } if(representative != nil) representative->DisposeDesignator(); return result; } // TAbstractScriptableObject::AECommandDispatch //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::AECommand: //---------------------------------------------------------------------------------------- TDescriptor TAbstractScriptableObject::AECommand(const TAETransaction& /*transaction*/, long /*aeCommandID*/, TAbstractScriptableObject* /* auxObjects = nil */, long /* auxInfo = 0 */) { TDescriptor nothing; Throw(errAEEventNotHandled); return nothing; } // TAbstractScriptableObject::AECommand //---------------------------------------------------------------------------------------- // TAbstractScriptableObject::CreateNewElement: //---------------------------------------------------------------------------------------- TAbstractScriptableObject* TAbstractScriptableObject::CreateNewElement(const TAETransaction& t, DescType newObjectClass, TDescriptor initialData, TDescriptor initialProperties, Boolean& usedInitialData, Boolean& usedInitialProperties) { TAbstractScriptableObject* newElement = nil; TAbstractScriptableObject* representative = nil; NOREGISTER(representative); OSErr representativeError = noErr; Try { representative = this->RepresentativeScriptingObject(t); if(representative != nil) newElement = representative->CreateNewElement(t, newObjectClass, initialData, initialProperties, usedInitialData, usedInitialProperties); } Catch(representativeError) { } if(representative != nil) representative->DisposeDesignator(); FailErr(representativeError); if(newElement == nil) Throw(errAEEventNotHandled); return newElement; } // TAbstractScriptableObject::CreateNewElement #pragma segment CFrontCruft